feat: send a DPoP proof at PAR by default (RFC 9449 §10.1) - #169
Merged
Conversation
client always committed the authorization code to its DPoP key at PAR via the plain dpop_jkt parameter (Option A) and never the alternative RFC 9449 §10.1 recognizes: presenting an actual DPoP proof at PAR (Option B). §10.1 itself recommends Option B — it reuses the same proof-building this client already does at the token and resource endpoints, and unlike dpop_jkt, it's real proof of possession at PAR time, not just a key identifier. Adds Config.PARDPoPBinding, defaulting (zero value) to the recommended PARDPoPBindingProof; PARDPoPBindingJKT keeps today's exact behavior for interop. Every authorization server supporting DPoP at PAR must accept both mechanisms (§10.1's own MUST) — server/par.go's reconcileParDPoPBinding already does, unchanged by this commit. Sending a proof at PAR means PAR can now be nonce-challenged the same way the token endpoint already can, so PARDPoPBindingProof gets the identical retry BeginAuthorization's ExchangeCode already has — rebuild the form (a client assertion is exactly as single-use as a DPoP proof) and resubmit once with the challenged nonce. Verified against the live OIDF conformance suite: the RP suites (which exercise this new default against a real, spec-compliant AS, not just this module's own) pass unchanged. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
clientalways bound the authorization code to its DPoP key at PAR via the plaindpop_jktparameter (RFC 9449 §10.1's Option A) and never the alternative the RFC itself recommends: presenting an actual DPoP proof at PAR (Option B) — simpler for the client (reuses the same proof-building already done at the token/resource endpoints) and, unlikedpop_jkt, real proof of possession at PAR time rather than a bare key identifier.Config.PARDPoPBinding, defaulting (zero value) to the recommendedPARDPoPBindingProof.PARDPoPBindingJKTkeeps today's exact behavior for interop with a deployment that has a specific reason to prefer it.serverneeds no changes —server/par.go'sreconcileParDPoPBindingalready accepts both mechanisms and requires an AS supporting DPoP at PAR to (RFC 9449 §10.1's own MUST).ExchangeCodealready has at the token endpoint (rebuild the form — a client assertion is exactly as single-use as a DPoP proof — and resubmit once with the challenged nonce).Test plan
go build ./...,go vet ./...,go test ./...all cleanTestBeginAuthorizationCommitsDPoPKeyAtPARMatchingTokenEndpointProofJKT(now explicit about testing Option A) and new tests: the same commitment property for the default Proof mode, PAR nonce-challenge retry, and config validation for the new field (including that the zero value is accepted and behaves as the default)cmd/conformance-clientdrives the real RP-conformance suite throughBeginAuthorizationunchanged: RP baseline (22/22 PASSED) and RP message-signing (28/28 PASSED) confirm the live suite's AS role accepts DPoP-header-based PAR binding. AS baseline (57 modules, 4465 conditions) and AS message-signing (71 modules, 5975 conditions) also 0 failures — no regressions anywhere.